package com.github.thorqin.reader.services

import android.app.*
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.getActivity
import android.content.Context
import android.content.Intent
import android.media.AudioManager
import android.media.MediaPlayer
import android.os.*
import android.speech.tts.TextToSpeech
import android.speech.tts.UtteranceProgressListener
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import android.view.KeyEvent
import com.github.thorqin.reader.App
import com.github.thorqin.reader.BuildConfig
import com.github.thorqin.reader.R
import com.github.thorqin.reader.activities.book.BookActivity
// MS SPEECH
import com.microsoft.cognitiveservices.speech.*
import com.microsoft.cognitiveservices.speech.audio.AudioConfig
import com.microsoft.cognitiveservices.speech.util.EventHandler
import java.util.*
import java.util.concurrent.Future


enum class Engine {
	DEVICE_TTS,
	MS_TTS
}


// Replace below with your own service region (e.g., "westus").
private const val serviceRegion = "japanwest"


class TTSService : Service()  {

	companion object {
		const val MEDIA_SESSION_ACTIONS = PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PAUSE
		const val NOTIFICATIONS_ID = 1
		const val CHANNEL_ID = "TTS-SERVICE-CHANNEL"
		const val CHANNEL_NAME = "EReader TTS"
		val voiceMap = mapOf(
			"晓辰（女）" to "zh-CN-XiaochenNeural",
			"晓晓（女）" to "zh-CN-XiaoxiaoNeural",
			"晓涵（女）" to "zh-CN-XiaohanNeural",
			"晓萱（女）" to "zh-CN-XiaoxuanNeural",
			"晓墨（女）" to "zh-CN-XiaomoNeural",
			"晓秋（女）" to "zh-CN-XiaoqiuNeural",
			"晓悠（女童）" to "zh-CN-XiaoyouNeural",
			"云希（男）" to "zh-CN-YunxiNeural",
			"云扬（男）" to "zh-CN-YunyangNeural",
			"云野（男）" to "zh-CN-YunyeNeural",
			"曉曼（粤语女）" to "zh-HK-HiuMaanNeural",
			"雲龍（粤语男）" to "zh-HK-WanLungNeural"
		)

	}


	interface StateListener {
		fun onStop(msg: String? = null)
		fun onStart()
		fun onUpdate()
	}

	class TTSBinder(val service: TTSService): Binder()
	private val ttsBinder = TTSBinder(this)

	private lateinit var tts: TextToSpeech
	private var playing = false

	private var playInfo: App.FileDetail? = null

	private var stateListener: StateListener? = null
	private var readingPos = 0L
	private var nextPos =0L

	private lateinit var handler: Handler
	private lateinit var mediaSession: MediaSessionCompat
	private val stateBuilder: PlaybackStateCompat.Builder= PlaybackStateCompat.Builder()
		.setActions(PlaybackStateCompat.ACTION_PLAY or PlaybackStateCompat.ACTION_PLAY_PAUSE)

	private lateinit var  phoneCallback: TelephonyCallback

	val isPlaying: Boolean get() = playing

	private fun createMediaSession() {
		mediaSession = MediaSessionCompat(this, "EReader").apply {
			setCallback(object: MediaSessionCompat.Callback() {
				override fun onPause() {
					if (playing) {
						stop(true)
						stateListener?.onStop()
					}
				}

				override fun onPlay() {
					if (!playing) {
						play(true)
						stateListener?.onStart()
					}
				}

				override fun onMediaButtonEvent(mediaButtonEvent: Intent?): Boolean {

					if (mediaButtonEvent == null) {
						return super.onMediaButtonEvent(mediaButtonEvent)
					}
					val keyEvent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
						mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT, KeyEvent::class.java) ?: return super.onMediaButtonEvent(mediaButtonEvent)
					} else {
						mediaButtonEvent.getParcelableExtra<KeyEvent>(Intent.EXTRA_KEY_EVENT) ?: return super.onMediaButtonEvent(mediaButtonEvent)
					}

					if (keyEvent.action == KeyEvent.ACTION_UP) {
						if (keyEvent.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE || keyEvent.keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
							if (playing) {
								stop(true)
								stateListener?.onStop()
							} else {
								play(true)
								stateListener?.onStart()
							}
							return true
						} else if (keyEvent.keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {
							if (playing) {
								stop(true)
								stateListener?.onStop()
								return true
							}
						} else if (keyEvent.keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {
							if (!playing) {
								play(true)
								stateListener?.onStart()
								return true
							}
						}
					}
					// println("media button event: ${mediaButtonEvent.action}, KeyCode: ${keyEvent.keyCode}")
					return super.onMediaButtonEvent(mediaButtonEvent)
				}
			})
			setPlaybackState(stateBuilder.build())
			setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS or MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)
			isActive = true
			val it = Intent(App.instance, BookActivity::class.java)
			setSessionActivity(getActivity(App.instance, 0, it, PendingIntent.FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE))
		}
	}

	private fun playSilentSound() {
		val player = MediaPlayer.create(this, R.raw.silent_sound)
		player.setOnCompletionListener { player.release() }
		player.start()
	}

	private var speechConfig: SpeechConfig? = null
	private var synthesizer: SpeechSynthesizer? = null

	private fun initMS() {
		if (speechConfig == null) {
			(application as App).config.let { config ->
				val voice = voiceMap[config.msEngineVoice] ?: "zh-CN-XiaochenNeural"
				speechConfig = SpeechConfig.fromSubscription(BuildConfig.SPEECH_KEY, serviceRegion)
				speechConfig!!.speechSynthesisVoiceName = voice
				speechConfig!!.setSpeechSynthesisOutputFormat(SpeechSynthesisOutputFormat.Raw16Khz16BitMonoPcm)
			}

		}
		if (synthesizer == null) {
			val audioConfig = AudioConfig.fromDefaultSpeakerOutput()
			synthesizer = SpeechSynthesizer(speechConfig, audioConfig)
			val listener = { p0: Any, event: SpeechSynthesisEventArgs ->
				when (event.result.reason) {
					ResultReason.SynthesizingAudioCompleted -> {
						println("播放完成: ${event.result.reason}")
						if (playing) {
							readingPos = nextPos
							if (playInfo != null) {
								playInfo!!.setTtsPosition(readingPos)
								(application as App).saveFileState(playInfo!!, playInfo!!.key)
								stateListener?.onUpdate()
								ttsPlay()
							}
						}
						event.result.close()
					}
					ResultReason.Canceled -> {
						println("播放取消: ${event.result.reason}")
						event.result.close()
					}
					else -> {}
				}

			}

			Connection.fromSpeechSynthesizer(synthesizer).openConnection(true)
			synthesizer!!.Synthesizing.addEventListener(listener)
			synthesizer!!.SynthesisCompleted.addEventListener(listener)
			synthesizer!!.SynthesisCanceled.addEventListener(listener)
		}
	}

	private fun initTTS(callback: (available: Boolean) -> Unit) {
		tts = TextToSpeech(this) {status ->
			if (status == TextToSpeech.SUCCESS) {
				val result = tts.setLanguage(Locale.CHINA)
				if (result != TextToSpeech.LANG_COUNTRY_AVAILABLE && result != TextToSpeech.LANG_AVAILABLE){
					callback(false)
					return@TextToSpeech
				}
				tts.setOnUtteranceProgressListener(object: UtteranceProgressListener() {
					override fun onDone(p0: String?) {
//				println("done: $p0")
						readingPos = nextPos
						if (playInfo != null && p0 == "e-reader-sentence") {
							playInfo!!.setTtsPosition(readingPos)
							(application as App).saveFileState(playInfo!!, playInfo!!.key)
							stateListener?.onUpdate()
							ttsPlay()
						}
					}

					override fun onError(p0: String?) {
						System.err.println("error: $p0")
					}

					override fun onStart(p0: String?) {
						// println("start: $p0")
					}

				})
				callback(true)
			} else {
				callback(false)
			}
		}


	}

	override fun onCreate() {
		super.onCreate()
		handler = Handler(Looper.getMainLooper())

		phoneCallback = object : TelephonyCallback(), TelephonyCallback.CallStateListener {
			override fun onCallStateChanged(state: Int) {
				if (state == TelephonyManager.CALL_STATE_RINGING) {
					val audioManager =  getSystemService(Context.AUDIO_SERVICE) as AudioManager
					if (audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0) {
						if (playing) {
							stop(true)
							stateListener?.onStop()
						}
					}
				}
			}
		}

		val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
		try {
			telephonyManager.registerTelephonyCallback(this.mainExecutor, phoneCallback)
		} catch (e: Throwable) {
			System.err.println(e.message)
			e.printStackTrace(System.err)
		}
		Binder.clearCallingIdentity()
		createMediaSession()
	}

	override fun onDestroy() {
		try {
			val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
			telephonyManager.unregisterTelephonyCallback(phoneCallback)
			mediaSession.release()
		} catch (_: Throwable) {

		}
		super.onDestroy()
	}

	override fun onBind(intent: Intent): IBinder {
		return ttsBinder
	}

	var msPlayResult: Future<SpeechSynthesisResult>? = null
	private fun ttsPlay() {

		while (playInfo != null && playing) {
			val info = playInfo as App.FileDetail
			val sentence = info.getTtsSentence(readingPos)
			if (sentence.sentence != null) {
//				println(sentence.sentence)
				val str = sentence.sentence!!.replace(Regex("[\\s\\n]+"), " ").trim()
				if (str.isNotEmpty()) {
					nextPos = sentence.nextPos
					if (engine == Engine.DEVICE_TTS) {
						tts.speak(str, TextToSpeech.QUEUE_FLUSH, null, "e-reader-sentence")
					} else if (engine == Engine.MS_TTS) {
						msPlayResult = synthesizer!!.SpeakTextAsync(str)
					}
					break
				} else {
					readingPos = sentence.nextPos
				}
			} else {
				stop(true)
				stateListener?.onStop()
				break
			}
		}

	}

	fun setFileInfo(fileInfo: App.FileDetail) {
		playInfo = fileInfo
	}

	private var engine: Engine = Engine.DEVICE_TTS

	private fun playTTS() {
		initTTS { ttsAvailable ->
			if (!ttsAvailable) {
				stop(true)
				stateListener?.onStop("初始化 TTS 引擎失败")
			} else {
				ttsPlay()
			}
		}
	}

	private fun stopTTS() {
		if (::tts.isInitialized) {
			tts.stop()
			tts.shutdown()
		}
	}

	private fun playMS() {
		initMS()
		ttsPlay()
	}

	private fun stopMS() {
		msPlayResult?.cancel(true)
		synthesizer!!.StopSpeakingAsync().get()
		synthesizer?.close()
		synthesizer = null
		speechConfig?.close()
		speechConfig = null
	}



	fun play(setup: Boolean = false) {
		if (playing) {
			stop(setup)
		}
		val info = playInfo ?: run {
			stop(true)
			return
		}
		playing = true
		readingPos = info.ttsPoint
		if (setup) {
			playSilentSound()
			mediaSession.setPlaybackState(PlaybackStateCompat.Builder()
				.setActions(MEDIA_SESSION_ACTIONS)
				.setState(PlaybackStateCompat.STATE_PLAYING, 0L, 1f).build())

			mediaSession.setMetadata(MediaMetadataCompat.Builder()
				.putText(MediaMetadataCompat.METADATA_KEY_TITLE, "Ereader TTS")
				.build())

			val notificationIntent = Intent(this, BookActivity::class.java)
			val pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, FLAG_IMMUTABLE)
			val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
			@Suppress("DEPRECATION")
			val builder = Notification.Builder(this)
				.setContentTitle(this.getString(R.string.app_name))
				.setContentText("正在使用语音播放图书...")
				.setSmallIcon(R.drawable.ic_book)
				.setContentIntent(pendingIntent)
			val notificationChannel = NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_MIN)
			notificationChannel.enableLights(false)
			notificationChannel.setShowBadge(false)
			notificationChannel.lockscreenVisibility = Notification.VISIBILITY_SECRET
			notificationManager.createNotificationChannel(notificationChannel)
			builder.setChannelId(CHANNEL_ID)
			val notification = builder.build()
			notificationManager.notify(NOTIFICATIONS_ID, notification)
			startForeground(NOTIFICATIONS_ID, notification)
		}
		mediaSession.isActive = true
		playing = true
		engine = if ((application as App).config.ttsEngine == "ms") {
			Engine.MS_TTS
		} else {
			Engine.DEVICE_TTS
		}
		if (engine == Engine.DEVICE_TTS) {
			playTTS()
		} else if (engine == Engine.MS_TTS) {
			playMS()
		}
	}

	fun stop(setup: Boolean = false) {
		if (playing) {
			playing = false
			if (setup) {
				mediaSession.setPlaybackState(
					PlaybackStateCompat.Builder()
						.setActions(MEDIA_SESSION_ACTIONS)
						.setState(PlaybackStateCompat.STATE_PAUSED, 0L, 1f).build()
				)
				stopForeground(false)
				val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
				notificationManager.cancel(NOTIFICATIONS_ID)
			}
			if (engine == Engine.DEVICE_TTS) {
				stopTTS()
			} else if (engine == Engine.MS_TTS) {
				stopMS()
			}
		}
	}

	fun setListener(listener: StateListener) {
		stateListener = listener
	}


}
